package com.github.tomakehurst.wiremock.db.utils;

import com.github.tomakehurst.wiremock.db.config.DatabaseMappingsSource;
import com.github.tomakehurst.wiremock.db.mapper.WireMockJournalMapper;
import com.github.tomakehurst.wiremock.db.service.MultiMappingService;
import com.github.tomakehurst.wiremock.db.service.StubMappingService;
import com.github.tomakehurst.wiremock.standalone.MappingsSource;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;

public class MyBatisUtil {
  private static SqlSessionFactory sqlSessionFactory;
  // 新增：Journal Mapper 类对象（用于注册DAO）
  private static final Class<?>[] JOURNAL_MAPPERS = {WireMockJournalMapper.class};

  /**
   * 初始化SqlSessionFactory（基于mybatis-config.xml配置）
   * 新增：自动注册 Journal 相关 Mapper，避免XML中手动配置
   */
  private static void initSqlSessionFactory() {
    if (sqlSessionFactory == null) {
      try {
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory tempFactory = builder.build(inputStream);

        // 注册 Journal Mapper（确保 MyBatis 能识别 DAO 接口）
        Configuration config = tempFactory.getConfiguration();
        registerJournalMappers(config);

        sqlSessionFactory = builder.build(config);
      } catch (IOException e) {
        throw new RuntimeException("Failed to initialize MyBatis from config file", e);
      }
    }
  }

  /**
   * 重载：通过数据源动态初始化SqlSessionFactory
   * 优化：保留原配置 + 替换数据源 + 注册 Journal Mapper
   */
  public static void initSqlSessionFactory(DataSource dataSource) {
    if (sqlSessionFactory == null) {
      try {
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        Configuration config = new SqlSessionFactoryBuilder().build(inputStream).getConfiguration();

        // 1. 替换数据源（保持环境ID、事务工厂不变）
        Environment newEnv = new Environment(
                config.getEnvironment().getId(),
                new JdbcTransactionFactory(), // 显式使用JDBC事务（确保事务可控）
                dataSource
        );
        config.setEnvironment(newEnv);

        // 2. 注册 Journal Mapper
        registerJournalMappers(config);

        // 3. 构建工厂
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
      } catch (IOException e) {
        throw new RuntimeException("Failed to initialize MyBatis with dynamic DataSource", e);
      }
    }
  }

  /**
   * 新增：注册 Journal 相关 Mapper（避免在 mybatis-config.xml 中手动配置）
   */
  private static void registerJournalMappers(Configuration config) {
    for (Class<?> mapper : JOURNAL_MAPPERS) {
      if (!config.hasMapper(mapper)) { // 避免重复注册
        config.addMapper(mapper);
      }
    }
  }

  /**
   * 获取 SqlSession（优化：默认不自动提交，由业务层控制事务）
   * 原因：Journal 的 add() 方法需同时插入主表+详情表，需手动控制事务提交
   */
  public static SqlSession getSqlSession() {
    initSqlSessionFactory();
    // 改为 false：不自动提交，业务层需手动调用 session.commit()
    return sqlSessionFactory.openSession(false);
  }

  /**
   * 重载：指定事务是否自动提交（灵活适配不同场景）
   */
  public static SqlSession getSqlSession(boolean autoCommit) {
    initSqlSessionFactory();
    return sqlSessionFactory.openSession(autoCommit);
  }

  /**
   * 获取数据库版 MappingsSource（原有逻辑不变）
   */
  public static MappingsSource getDatabaseMappingsSource() {
    initSqlSessionFactory();
    StubMappingService stubService = new StubMappingService(sqlSessionFactory);
    MultiMappingService multiService = new MultiMappingService(sqlSessionFactory);
    return new DatabaseMappingsSource(stubService, multiService);
  }

  /**
   * 重载：通过数据源获取 MappingsSource（原有逻辑不变）
   */
  public static MappingsSource getDatabaseMappingsSource(DataSource dataSource) {
    initSqlSessionFactory(dataSource);
    StubMappingService stubService = new StubMappingService(sqlSessionFactory);
    MultiMappingService multiService = new MultiMappingService(sqlSessionFactory);
    return new DatabaseMappingsSource(stubService, multiService);
  }

  /**
   * 新增：获取 SqlSessionFactory（供 Journal 存储等外部组件使用）
   */
  public static SqlSessionFactory getSqlSessionFactory() {
    initSqlSessionFactory();
    return sqlSessionFactory;
  }
}